// Copyright 2012, Cheng Chu.  All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Cheng Chu (zunceng at gmail dot com)

#ifndef __MSG_BUFFER_HPP__
#define __MSG_BUFFER_HPP__


#include <iostream>
#include <assert.h>

//
// The wire format of an PDP (poi db protocol) header is:
// 
// 0               4               8                             16
// +---------------+---------------+---------------+--------------+      ---
// |C|S|         |T|               |               |              |       ^
// | |U| reseve  | |  total_length |     sequnce   |       ID     |
// | |M|         | |               |               |              |       |
// +---------------+---------------+------------------------------+    1024 bytes
// |                                                              |       |
// |                              data                            |           
// |                                                              |       v
// +-------------------------------+------------------------------+      ---

//-----------------------------------------------------------------------------

// C is used to press is this package last partition.
// Sum is the check_sum.
// T is the type of the package.
// only TANSPORT package use the ID , other package's id is 0. 
// every TANSPORT package is given the ID by master program.
// total_length and sequnce is used to part the package which is big than 1024 bits.

enum{ BUFFER_BLOCK_SIZE = 256};
enum{ HEAD_SIEZ = 16};
enum{ DATA_SIZE = BUFFER_BLOCK_SIZE - HEAD_SIEZ};
enum{ CHECKSUM_WORD = 1};
enum{ TYPE_WORD = 3};
enum{ FLAG_WORD = 0};

//type
enum{ REGIST = 1, HEARTBEAT = 2, TANSPORT = 3};

struct MsgBuffer
{
	unsigned char buffer[BUFFER_BLOCK_SIZE];

	unsigned int type() const {return buffer[TYPE_WORD] & 0xFF;}

	bool LastPack() const {return buffer[FLAG_WORD] & 0xFF;}

	unsigned int total_length() const {return decode(4, 5, 6, 7);}

	unsigned int sequnce() const {return decode(8, 9, 10, 11);}

	unsigned int id() const {return decode(12, 13, 14, 15);}

	char* head() { return reinterpret_cast<char *>(buffer);}
	char* data() { return reinterpret_cast<char *>(buffer + HEAD_SIEZ);}

	void type(unsigned int _type) {buffer[TYPE_WORD] = _type;}

	void total_length(unsigned int _total_length){ encode(_total_length, 4);}

	void sequnce(unsigned int _sequnce){ encode(_sequnce, 8);}

	void id(unsigned int _id) {encode(_id, 12);}

	void LastPack(bool islastpack){ buffer[FLAG_WORD] = islastpack;}

	//unsigned char check_sum() const
	//{
	//	return buffer[CHECKSUM_WORD] & 0xFF;
	//}

	//void write_check_sum()
	//{
	//	unsigned char sum = 0;
	//	for (int i = 0; i < BUFFER_BLOCK_SIZE; i++)
	//	{	
	//		if (i == CHECKSUM_WORD)
	//		{
	//			sum += buffer[i] & 0xFF;
	//		}
	//	}
	//	memcpy(buffer + CHECKSUM_WORD, &sum, sizeof(char));
	//}

	void bzero()
	{
		memset(buffer, 0, BUFFER_BLOCK_SIZE);
	}

	unsigned int length()
	{
		unsigned int len = total_length() - sequnce();
		len = len > DATA_SIZE ? DATA_SIZE : len;
		return len;
	}

private:
	unsigned int decode(int a, int b, int c, int d) const
	{
		return	(buffer[a] << 24)	| 
				(buffer[b] << 16)	|
				(buffer[c] << 8)	|
				(buffer[d]);
	}
	void encode(unsigned int t,unsigned int p)
	{
		assert(p < BUFFER_BLOCK_SIZE);
		buffer[ p ] = (t >> 24) & 0xFF;
		buffer[p+1] = (t >> 16) & 0xFF;
		buffer[p+2] = (t >> 8 ) & 0xFF;
		buffer[p+3] = t         & 0xFF;
	}
};// end of MsgBuffer

struct Msg{
	std::string msg;
	int id;

	void clear()
	{
		msg.clear();
		id = 0;
	}
};// end of Msg



#endif // end of __MSG_BUFFER_HPP__